using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
using System.Diagnostics.CodeAnalysis;

namespace CoolEngine.Engine
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class Camera : Microsoft.Xna.Framework.GameComponent
    {
        public static Camera ActiveCamera { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="Camera"/> class.
        /// </summary>
        /// <param name="game">Game that the game component should be attached to.</param>
        public Camera(Game game)
            : this(Vector3.Zero, Vector3.Forward, Vector3.Up, Vector3.Right, (float)(90 * Math.PI / 180.0),1,10000, game)
        {

        }
        public Camera(Vector3 position, Vector3 forward, Vector3 up, Vector3 right, float fieldOfView, float near, float far,Game game)
            : base(game)
        {
            Position = position;
            Forward = forward;
            Up = up;
            Right = right;
            FieldOfView = fieldOfView;
            NearPlane = near;
            FarPlane = far;
            ActiveCamera = this;
        }
        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            AspectRatio = Game.GraphicsDevice.Viewport.AspectRatio;

            UpdateOrder = Int32.MinValue;
            SetMatrices();
            
            base.Initialize();
        }
        private Color tint = Color.White;
        public Color Tint { get { return tint; } set { tint = value; } }
        public float AspectRatio { get; set; }
        public Vector3 Position { get; set; }
        public float FieldOfView { get; set; }
        public Vector3 Forward { get; set; }
        public Vector3 Right { get; set; }
        public Vector3 Up { get; set; }
        public float NearPlane { get; set; }
        public float FarPlane { get; set; }
        public Matrix World { get; private set; }
        public Matrix View { get; private set; }
        public Matrix Projection { get; private set; }
        public Matrix ViewProjection { get; private set; }
        public BoundingFrustum Frustum { get; private set; }
        private Matrix workingMatrix;
        public Vector3 Target
        {
            get { return Position + Forward; }
            set
            {
                LookAt(value);  
            }
        }
        [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "WorldView")]
        public Matrix WorldViewProjection { get; private set; }
        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            SetMatrices();
            
            base.Update(gameTime);
        }
        public void LookAt(float x, float y, float z)
        {
            this.LookAt(new Vector3(x, y, z));
        }
        public void LookAt(Vector3 target)
        {
            Vector3 direction = (target - Position);
            direction.Normalize();
            Forward = direction;
            Right = Vector3.Cross(Forward, Up);
        }
        public void Pan(float rads)
        {
            workingMatrix = Matrix.CreateFromAxisAngle(Up, rads);
            Forward = Vector3.Transform(Forward, workingMatrix);
        }
        //public void CircleTarget(Vector3 target, float rads)
        //{
        //    workingMatrix = Matrix.CreateFromAxisAngle(Up, -rads);
        //    Vector3 modPos = Position - target;
        //    Matrix.
        //}
        private void SetMatrices()
        {
            World = Matrix.CreateTranslation(Position);
            View = Matrix.CreateLookAt(Position, Position + Forward, Up);
            Projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView, AspectRatio, NearPlane, FarPlane);
            ViewProjection = View * Projection;
            Frustum.Matrix = ViewProjection;
            WorldViewProjection = World * ViewProjection;
        }
    }
}